﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WayPointCreator
{
    public partial class Form1 : Form
    {
        private const int MAP_X_OFFSET = 32;
        private const int MAP_Y_OFFSET = 32;
        private const int WP_SIZE = 5;
        private List<WayPoint> waypoints;
        private WayPoint focusedWayPoint;
        private Bitmap bitmap;

        private WayPoint wpOne, wpTwo;
        private Point lineEnd;
        private Pen linePen = Pens.Black;

        public Form1()
        {
            InitializeComponent();
            this.waypoints = new List<WayPoint>();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog ofd = new OpenFileDialog())
            {
                if (ofd.ShowDialog() == DialogResult.OK)
                {
                    this.bitmap = (Bitmap)Bitmap.FromFile(ofd.FileName);
                    this.Refresh();
                }
            }
        }

        private WayPoint GetFocusedWayPoint(Point p)
        {
            WayPoint waypoint = null;
            for (int i = 0; i < this.waypoints.Count; i++)
            {
                if (this.waypoints[i].x < p.X &&
                    this.waypoints[i].x + (WP_SIZE*2) > p.X &&
                    this.waypoints[i].y  < p.Y &&
                    this.waypoints[i].y + (WP_SIZE*2) > p.Y)
                {
                    waypoint = this.waypoints[i];
                }
            }

            return waypoint;
        }

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {


        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void Form1_Click(object sender, EventArgs e)
        {

        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            if (this.bitmap != null)
            {
                Graphics g = e.Graphics;
                g.DrawImage(this.bitmap, MAP_X_OFFSET, MAP_Y_OFFSET, this.bitmap.Width, this.bitmap.Height);
                for (int i = 0; i < this.waypoints.Count; i++)
                {
                    if (this.waypoints[i] == this.focusedWayPoint)
                    {
                        g.FillEllipse(Brushes.Yellow, this.waypoints[i].x, this.waypoints[i].y, WP_SIZE * 2, WP_SIZE * 2);
                    }
                    else
                    {
                        g.FillEllipse(Brushes.Red, this.waypoints[i].x, this.waypoints[i].y, WP_SIZE * 2, WP_SIZE * 2);
                    }

                    for (int j = 0; j < this.waypoints[i].adjacent.Count; j++)
                        g.DrawLine(this.linePen, this.waypoints[i].x + WP_SIZE, this.waypoints[i].y + WP_SIZE, this.waypoints[i].adjacent[j].x + WP_SIZE, this.waypoints[i].adjacent[j].y + WP_SIZE);
                }

                if (this.wpOne != null)
                {
                    g.DrawLine(this.linePen, this.wpOne.x + WP_SIZE, this.wpOne.y + WP_SIZE, this.lineEnd.X, this.lineEnd.Y);
                }
            }
        }

        private void Form1_MouseClick(object sender, MouseEventArgs e)
        {
            Point p = e.Location;
            if (e.Button == MouseButtons.Left && this.bitmap != null)
            {
                WayPoint waypoint = this.GetFocusedWayPoint(p);
                if (waypoint == null)
                {
                    if (p.X > MAP_X_OFFSET &&
        p.X < MAP_X_OFFSET + this.bitmap.Width &&
        p.Y > MAP_Y_OFFSET &&
        p.Y < MAP_Y_OFFSET + this.bitmap.Height)
                    {
                        WayPoint wp = new WayPoint();
                        wp.x = p.X - WP_SIZE;
                        wp.y = p.Y - WP_SIZE;
                        this.waypoints.Add(wp);
                    }

                }
                this.Refresh();
            }

        }

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            if (this.bitmap != null)
            {
                Point p = e.Location;
                if (p.X > MAP_X_OFFSET &&
                    p.X < MAP_X_OFFSET + this.bitmap.Width &&
                    p.Y > MAP_Y_OFFSET &&
                    p.Y < MAP_Y_OFFSET + this.bitmap.Height)
                {
                    this.focusedWayPoint = this.GetFocusedWayPoint(p);
                    this.Refresh();

                    if (this.wpOne != null)
                    {
                        this.lineEnd = p;
                    }
                }
            }

        }

        private void Form1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            WayPoint wp = this.GetFocusedWayPoint(e.Location);
            if (wp != null)
            {
                this.waypoints.Remove(wp);
                for (int i = 0; i < this.waypoints.Count; i++)
                {
                    if (this.waypoints[i].adjacent.Contains(wp))
                        this.waypoints[i].adjacent.Remove(wp);
                }
            }
        }

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                Point p = e.Location;
                if (p.X > MAP_X_OFFSET &&
                    p.X < MAP_X_OFFSET + this.bitmap.Width &&
                    p.Y > MAP_Y_OFFSET &&
                    p.Y < MAP_Y_OFFSET + this.bitmap.Height)
                {
                    if (this.wpOne == null)
                    {
                        this.wpOne = this.GetFocusedWayPoint(p);
                    }

                }
            }

        }

        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {

                Point p = e.Location;
                if (p.X > MAP_X_OFFSET &&
                    p.X < MAP_X_OFFSET + this.bitmap.Width &&
                    p.Y > MAP_Y_OFFSET &&
                    p.Y < MAP_Y_OFFSET + this.bitmap.Height)
                {
                    if (wpOne != null)
                    {
                        WayPoint wpTwo = this.GetFocusedWayPoint(p);
                        if (wpTwo != null)
                        {
                            if (!wpOne.adjacent.Contains(wpTwo))
                            {
                                wpOne.adjacent.Add(wpTwo);
                                wpTwo.adjacent.Add(wpOne);
                            }
                        }

                    }
                }
                this.wpOne = null;
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Form form = new Form();
                TextBox textXML = new TextBox();
                textXML.Multiline = true;
                textXML.Text = this.BuildXml();
                textXML.Dock = DockStyle.Fill;
                form.Controls.Add(textXML);
                form.Show();
        }

        private string BuildXml()
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendLine("<WayPoints>");
            for (int i = 0; i < this.waypoints.Count; i++)
            {
                builder.AppendFormat("<WayPoint>{3}<Position>{3}<X>{0}</X>{3}<Y>{1}</Y>{3}</Position>{3}<ID>{2}</ID>",
                    ((this.waypoints[i].x + WP_SIZE) - MAP_X_OFFSET) / (float)this.bitmap.Width,
                    ((this.waypoints[i].y + WP_SIZE) - MAP_Y_OFFSET) / (float)this.bitmap.Height,
                    i,
                    Environment.NewLine);
                if (this.waypoints[i].adjacent.Count > 0)
                {
                    builder.AppendLine("<AdjacentWaypoints>");
                    for (int j = 0; j < this.waypoints[i].adjacent.Count; j++)
                    {
                        builder.AppendFormat("<int>{0}</int>{1}", this.waypoints.IndexOf(this.waypoints[i].adjacent[j]), Environment.NewLine);
                    }
                    builder.AppendLine("</AdjacentWaypoints>");
                }
                builder.AppendLine("</WayPoint>");
            }
            builder.AppendLine("</WayPoints>");
            return builder.ToString();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            using (ColorDialog cd = new ColorDialog())
            {
                if (cd.ShowDialog() == DialogResult.OK)
                {
                    this.linePen = new Pen(cd.Color);
                }
            }
        }

        private void button4_Click(object sender, EventArgs e)
        {
            using (LoadDialog ld = new LoadDialog())
            {
                if (ld.ShowDialog() == DialogResult.OK)
                {
                    this.ParseXML(ld.XML);
                }
            }
        }

        private void ParseXML(string xml)
        {
            System.Globalization.CultureInfo currentCulture = System.Globalization.CultureInfo.CurrentCulture;
            System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
            Dictionary<int, int[]> adjacent = new Dictionary<int, int[]>();
            SortedList<int, WayPoint> points = new SortedList<int,WayPoint>();
            System.IO.StringReader stringReader = new System.IO.StringReader(xml);
            System.Xml.XmlReader reader = System.Xml.XmlReader.Create(stringReader);

            while (reader.Read())
            {
                if (reader.Name.Equals("WayPoint", StringComparison.CurrentCultureIgnoreCase))
                    this.ReadWayPoint(reader, adjacent, points);
            }
            for (int i = 0; i < points.Count; i++)
            {
                this.waypoints.Add(points[i]);
            }

            for (int i = 0; i < points.Count; i++)
            {
                for (int j = 0; j < adjacent[i].Length; j++)
                {
                    this.waypoints[i].adjacent.Add(this.waypoints[adjacent[i][j]]);
                }
            }
            System.Threading.Thread.CurrentThread.CurrentCulture = currentCulture;
        }

        private void ReadWayPoint(System.Xml.XmlReader reader, Dictionary<int, int[]> adjacent, SortedList<int, WayPoint> points)
        {
            WayPoint wp = new WayPoint();
            int ID = 0;
            List<int> adjacentPoints = new List<int>();
            while (reader.Read() && !reader.Name.Equals("WayPoint"))
            {
                
                if (reader.Name.Equals("Position", StringComparison.CurrentCultureIgnoreCase))
                {
                    reader.Read(); // ignore newline.
                    reader.Read(); // X node.
                    reader.Read(); // value.
                    wp.x = (int)(Math.Round(MAP_X_OFFSET + (float.Parse(reader.Value) * this.bitmap.Width), 0));
                    reader.Read(); // X closing node.
                    reader.Read(); // ignore newline.
                    reader.Read(); // Y node.
                    reader.Read(); // value.
                    wp.y = (int)(Math.Round(MAP_Y_OFFSET + (float.Parse(reader.Value) * this.bitmap.Height), 0));
                    reader.Read(); // Y closing node.
                    reader.Read(); // ignore newline.
                    reader.Read(); // Ignore closing position tag.
                }
                else if (reader.Name.Equals("AdjacentWaypoints", StringComparison.CurrentCultureIgnoreCase))
                {
                    int i;
                    while (reader.Read() && !reader.Name.Equals("AdjacentWaypoints", StringComparison.CurrentCultureIgnoreCase))
                    {
                        if (int.TryParse(reader.Value, out i))
                        {
                            adjacentPoints.Add(i);
                        }
                    }
                }
                else if (reader.Name.Equals("ID", StringComparison.CurrentCultureIgnoreCase))
                {
                    reader.Read(); // Value.
                    ID = int.Parse(reader.Value);
                    reader.Read(); // closing tag.
                }
            }
            points.Add(ID, wp);
            adjacent.Add(ID, adjacentPoints.ToArray());
        }

    }
}
